package algorithms;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import Entities.Calendar;

public class HillClimbing implements IAlgorithm{
	protected int maxOfHeuristicCalls = 0;
	protected int numberOfHeuristicCalls = 0;
	protected static Random rand = new Random();
	private int numberOfHeuristicCallsToBest = 0;
	
	/**
	 * This function implements simple HillClimbing, continuing until finding best neighbour
	 */
	public Calendar searchLocal(Calendar startCalendar) {		
		Calendar currCalendar = startCalendar;		
		Calendar nextNeighbour = null, bestNeighbour = startCalendar; // initialization
		List<Calendar> equalBestCalendars = new LinkedList<Calendar>();
		// climb the mountain
		while (true){ // as long we improve our heuristic
			// search in immediate neighbours for best one
			while ((nextNeighbour = currCalendar.getNextAdjacentCalendar()) != null)
			{
				numberOfHeuristicCalls++;
				if (maxOfHeuristicCalls < numberOfHeuristicCalls){ // reached max calls
					break;
				}
				if (!bestNeighbour.isBetter(nextNeighbour)){
					// case 1: next is same
					if (nextNeighbour.isSame(bestNeighbour)){
						equalBestCalendars.add(nextNeighbour);
					}
					// case 2: next is better than best
					else{
						bestNeighbour = nextNeighbour;
						equalBestCalendars.clear();
						equalBestCalendars.add(bestNeighbour);
					}
				}
			}
			if (currCalendar.isSame(bestNeighbour)){ 
				// no improve can be done, must reach here sometime because the graph is final
				return currCalendar;
			}
			currCalendar = randFromBestCalendars(equalBestCalendars);
			numberOfHeuristicCallsToBest = numberOfHeuristicCalls;
			// init equalBestCalendars for the next iteration
			equalBestCalendars.clear();
		}
	}
	
	private Calendar randFromBestCalendars(List<Calendar> equalBestCalendars) {
		int chosenCalendar = rand.nextInt(equalBestCalendars.size());
		return equalBestCalendars.get(chosenCalendar);
	}
	
	@Override
	public Calendar searchLocal(){
		return searchLocal(new Calendar(null));
	}
	
	public void setMaxOfHeuristicCalls(int numberToSet){
		this.maxOfHeuristicCalls = numberToSet;
	}

	public String toString(){
		return "Hill Climbing";
	}

	@Override
	public void reset() {
		numberOfHeuristicCalls = 0;
		numberOfHeuristicCallsToBest = 0;
	}
	
	public static void initSeed(){
		rand.setSeed(rand.nextLong());
	}

	public int getNumberOfHeuristicCalls() {
		return numberOfHeuristicCalls;
	}
	
	@Override
	public int getNumberOfHeuristicCallsToBest() {
		return numberOfHeuristicCallsToBest;
	}
}
